package xin.fsqr520.project.websocket;

import lombok.extern.slf4j.Slf4j;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import xin.fsqr520.project.domain.po.WsChatMessage;
import xin.fsqr520.project.domain.po.WsMessageType;
import xin.fsqr520.project.domain.vo.WsResult;
import xin.fsqr520.project.mapper.WschatMessageMapper;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.time.LocalDateTime;
import java.util.*;

/**
 * WebSocket服务
 */
@Slf4j
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {

    /*    @Autowired
        private WschatMessageMapper wschatMessageMapper;
        今天在项目中，遇见需要在websocket中注入其他的类，最后发现通过@Autowired无法注入，
        最后网上搜寻了下答案，大致原因为spring管理的都是单例（singleton），
        和 websocket （多对象）相冲突。因为websocket是多实例单线程的，而websocket中的对象
        在@Autowried时，只有整个项目启动时会注入，而之后新的websocket实例都不会再次注入，
        故websocket上@Autowried的bean是会为null的
        */
    private static WschatMessageMapper wschatMessageMapper;


    @Autowired
    public void setWschatMessageMapper(WschatMessageMapper wschatMessageMapper) {
        WebSocketServer.wschatMessageMapper = wschatMessageMapper;
    }

    //存放会话对象
    private static final Map<String, Session> sessionMap = new HashMap();

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen     //sid用于区分不同的客户端
    public void onOpen(Session session, @PathParam("sid") String sid) {
        log.info("客户端；id={}建立连接", sid);
        //向前端发送连接成功的message
        String result = WsResult.success(WsMessageType.CONNECT_SUCCESS, "server", sid, "与服务器连接成功").toJson();
        sendToClient(result, session);
        sessionMap.put(sid, session);
    }

    @OnMessage
    public void onMessage(String jsonMessage, @PathParam("sid") String senderSid) {
        try {
            // 解析JSON消息
            JSONObject messageObj = new JSONObject(jsonMessage);
            String msgType = messageObj.getString("type"); // 消息类型
            String msgContent = messageObj.getString("content");  // 消息内容

            if (msgType.equals(WsMessageType.PRIVATE.toString())) {
                // 私聊消息
                String receiverSid = messageObj.getString("receiver");
                forwardPrivateMessage(senderSid, receiverSid, msgContent);
            }

        } catch (Exception e) {
            String result = WsResult.error(WsMessageType.SEND_ERROR, "server", senderSid, "发送失败|消息格式错误" + e.getMessage()).toJson();
            sendToClient(result, sessionMap.get(senderSid));
        }
    }

    private void forwardPrivateMessage(String senderSid, String receiverSid, String content) {
        Session senderSession = sessionMap.get(senderSid);
        Session receiverSession = sessionMap.get(receiverSid);

        if (receiverSession == null) {
            String result = WsResult.error(WsMessageType.SEND_ERROR, "server", senderSid, "用户" + receiverSid + "不在线，消息存入数据库").toJson();
            sendToClient(result, sessionMap.get(senderSid));
            //如果对方不在线 直接存入数据库了
            //然后插入数据库
            WsChatMessage wsChatMessage = WsChatMessage.builder()
                    .sender(senderSid)
                    .receiver(receiverSid)
                    .messageType(WsMessageType.PRIVATE.toString())
                    .content(content)
                    .sendTime(LocalDateTime.now())
                    .build();
            wschatMessageMapper.insert(wsChatMessage);
            return;
        }
        //应该吧消息封装一下 然后发出去
        //然后插入数据库
        WsChatMessage wsChatMessage = WsChatMessage.builder()
                .sender(senderSid)
                .receiver(receiverSid)
                .messageType(WsMessageType.PRIVATE.toString())//私聊
                .content(content)
                .sendTime(LocalDateTime.now())
                .build();
        wschatMessageMapper.insert(wsChatMessage);
        log.info("sql:{}", wsChatMessage);
        // 构造转发消息的JSON
        String msg = WsResult.success(WsMessageType.PRIVATE, senderSid, receiverSid, content, wsChatMessage).toJson();
        // 发送给接收方
        sendToClient(msg, receiverSession);
        log.info("客户端 id={} 向客户端 id={},发送了消息：{}", senderSid, receiverSid, content);


        //向前端发送消息发送成功的message
        String result = WsResult.success(WsMessageType.SEND_SUCCESS, "server", senderSid, "消息发送成功", wsChatMessage).toJson();
        sendToClient(result, senderSession);
    }


    /**
     * 连接关闭调用的方法
     *
     * @param sid
     */
    @OnClose
    public void onClose(@PathParam("sid") String sid) {
        log.info("客户端；id={}断开连接", sid);
        sessionMap.remove(sid);
    }

    /**
     * 群发
     *
     * @param message
     */
    public void sendToAllClient(String message) {
        //此方法不是回调方法,需要手动调用
        Collection<Session> sessions = sessionMap.values();
        for (Session session : sessions) {
            try {
                //服务器向客户端发送消息
                session.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 给一个session发消息
     *
     * @param message
     */
    public void sendToClient(String message, Session session) {
        try {
            session.getBasicRemote().sendText(message);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
